<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>js继承</title>
</head>

<body>

    <script>
        // 1、原型链继承：通过原型链继承多个引用类型的属性和方法 （原型对象是另一个对象的实例）
        // 就是父类函数的实例当作子类函数的原型对象
        (function() {
            function SuperType() {
                this.property = true
            }

            SuperType.prototype.getSuperValue = function() {
                return this.property
            }

            function SubType() {
                this.subproperty = false
            }

            // 继承SuperType
            SubType.prototype = new SuperType()
            SubType.prototype.getSubValue = function() {
                return this.subproperty
            }

            let instance = new SubType()
            console.log(instance.getSubValue()) // false  自己的属性
            console.log(instance.getSuperValue()) // true 自己没有，往原型上找

            // 缺点1：原型对象中如果包含引用属性，当该属性更改后，那么所有实例都会受到影响。
            // 缺点2：子类型在实例化时不能给父类型的构造函数传参

        })();


        // 2、借用构造函数继承：在子类构造函数中调用父类构造函数
        (function() {
            function SuperType() {
                this.colors = ["red", "blue", "green"]
            }

            function SubType() {
                // 开始继承SuperType
                SuperType.call(this)
            }

            let instance = new SubType()
            instance.colors.push("black")

            console.log(instance.colors)

            let instance2 = new SubType()
            console.log(instance2.colors)
                // 缺点1：必须在构造函数中定义方法，因此函数不能重用
                // 缺点2：子类也不能访问父类原型上定义的方法

        })();
        // 3、组合继承-使用最多的继承模式：使用原型链继承原型上的属性和方法，通过借用构造函数继承实例属性
        (function() {
            function SuperType(name) {
                this.name = name
                this.colors = ["red", "blue", "green"]
            }
            SuperType.prototype.sayName = function() {
                console.log(this.name)
            }

            function SubType(name, age) {
                SuperType.call(this, name)
                this.age = age
            }

            SubType.prototype = new SuperType()
            SubType.prototype.sayAge = function() {
                console.log(this.age)
            }

            let instance1 = new SubType("Nicholas", 29)
            instance1.colors.push("black")
            instance1.sayName()
            instance1.sayAge()

            let instance2 = new SubType("Grey", 27)
            console.log(instance2.colors)
            instance2.sayName()
            instance2.sayAge()

            // 有一定的效率问题，父类构造函数会被调用两次，第一次是给子类设置原型时，第二次是给借用构造函数时

        })();

        // 4、寄生式组合继承：通过借用构造函数继承属性，但是使用混合式原型链继承方法。基本思路是不通过调用父类
        // 的构造函数给子类原型赋值，而是取得父类型的一个副本

        // 核心方法
        function inheritPrototype(subType, superType) {
            let prototype = Object.create(superType.prototype)
            prototype.constructor = subType
            subType.prototype = prototype
        }

        function SuperType(name) {
            this.name = name;
            this.colors = ["red", "blue", "green"];
        }
        SuperType.prototype.sayName = function() {
            console.log(this.name);
        };

        function SubType(name, age) {
            SuperType.call(this, name);
            this.age = age;
        }
        inheritPrototype(SubType, SuperType);
        SubType.prototype.sayAge = function() {
            console.log(this.age);
        };


        // Object.create 方法
        // 创建一个新对象,新对象的原型是第一个参数,第二个参数是配置参数
        function Base(name) {
            this.name = name
        }
        let newBase = Object.create(Base)
        console.log(newBase)


        let biped = {
            numLegs: 2
        };
        let person = Object.create(biped);
        person.name = 'Matt';
        console.log(person)
        console.log(person.name); // Matt
        console.log(person.numLegs); // 2
        console.log(Object.getPrototypeOf(person) === biped); // true
    </script>

</body>

</html>